#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <signal.h>

#include "config.h"
#include "compiler.h"
#include "simulate.h"

/* equivalent to strcpy(x, y); return x + strlen(y), but faster and safer */
/* Code like:
 *
 * char buf[256];
 * strcpy(buf, ...);
 * strcat(buf, ...);
 * strcat(buf, ...);
 *
 * Should be replaced with:
 *
 * char buf[256];
 * char *p, *end = EndOf(buf);
 * p = strput(buf, end, ...);
 * p = strput(p, end, ...);
 * p = strput(p, end, ...);
 */
char *strput(char *x, char *limit, char *y) {
	while ((*x++ = *y++)) {
		if (x == limit) {
			*(x-1) = 0;
			break;
		}
	}
	return x - 1;
}

char *strput_int(char *x, char *limit, int num) {
	char buf[20];
	sprintf(buf, "%d", num);
	return strput(x, limit, buf);
}

static FILE *debug_message_fp = 0;

void debug_message(char *fmt, ...)
{
	static char deb_buf[100];
	static char *deb = deb_buf;
	va_list args;

	if (!debug_message_fp) {
		/*
		 * check whether config file specified this option
		 */
		if (strlen(DEBUG_LOG_FILE))
			sprintf(deb, "%s/%s", LOG_DIR, DEBUG_LOG_FILE);
		else
			sprintf(deb, "%s/debug.log", LOG_DIR);
		while (*deb == '/')
			deb++;
		debug_message_fp = fopen(deb, "w");
		if (!debug_message_fp) {
			/* darn.  We're in trouble */
			perror(deb);
			abort();
		}
	}

	va_start(args, fmt);
	vfprintf(debug_message_fp, fmt, args);
	fflush(debug_message_fp);
	va_end(args);
	va_start(args, fmt);
	vfprintf(stderr, fmt, args);
	fflush(stderr);
	va_end(args);
}

void fatal(char *fmt, ...)
{
	static int in_fatal = 0;
	char msg_buf[2049];
	va_list args;

	if (in_fatal) {
		debug_message("Fatal error while shutting down.  Aborting.\n");
	} else {
		in_fatal = 1;
		va_start(args, fmt);
		vsprintf(msg_buf, fmt, args);
		va_end(args);
		debug_message("******** FATAL ERROR: %s\nMudOS driver attempting to exit gracefully.\n", msg_buf);
		if (current_file)
			debug_message("(occured during compilation of %s at line %d)\n", current_file, current_line);

		/* TODO
		dump_trace(1);
		*/
	}
	/* Make sure we don't trap our abort() */
	signal(SIGABRT, SIG_DFL);
	signal(SIGILL, SIG_DFL);
	signal(SIGIOT, SIG_DFL);

#if !defined(DEBUG)
	abort();
#endif
	in_fatal = 0;
}
